Skip to content

fix: preserve empty projection when ser/de HashJoinExec and NestedLoopJoinExec#23082

Open
haohuaijin wants to merge 3 commits into
apache:mainfrom
haohuaijin:fix-hashjoin-projection
Open

fix: preserve empty projection when ser/de HashJoinExec and NestedLoopJoinExec#23082
haohuaijin wants to merge 3 commits into
apache:mainfrom
haohuaijin:fix-hashjoin-projection

Conversation

@haohuaijin

@haohuaijin haohuaijin commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

HashJoinExec and NestedLoopJoinExec carry projection: Option<Vec<usize>> but the proto field is repeated uint32. Proto3 can't tell None from Some(vec![]), and the decoder treats both as None. Some(vec![]) is reachable in real plans — try_embed_projection produces it for SELECT count(1) … JOIN … (#20191) — and after a round-trip the join silently switches from "emit zero columns" to "emit all columns".

FilterExec has a workaround for the same limitation; these two execs were missed.

What changes are included in this PR?

Encode Some(vec![]) as the single-element sentinel [u32::MAX] (never a valid column index); recognise it on decode. Everything else goes through unchanged.

// encode
projection: match exec.projection.as_ref() {
    None => Vec::new(),
    Some(v) if v.is_empty() => vec![u32::MAX],
    Some(v) => v.iter().map(|x| *x as u32).collect(),
},

// decode
let projection = match hashjoin.projection.as_slice() {
    [] => None,
    [u32::MAX] => Some(Vec::new()),
    indices => Some(indices.iter().map(|i| *i as usize).collect()),
};

Applied symmetrically to HashJoinExec and NestedLoopJoinExec.

Are these changes tested?

yes, add roundtrip test case

Are there any user-facing changes?

@github-actions github-actions Bot added the proto Related to proto crate label Jun 22, 2026

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me -- thanks @haohuaijin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

proto Related to proto crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HashJoinExec / NestedLoopJoinExec projection Some(vec![]) becomes None after ser/de

2 participants